#!/usr/bin/perl -w
use Math::Combinatorics qw(permute);
#引入排列模块

if (@ARGV == 0){
	die "错误:No parameter!Enter \"?\" for help.\n";
}
#若无参数，提示错误并退出

if (@ARGV == 1){
	if ($ARGV[0] eq '?'){
		&help;
	}else{die "Error:At least 2 parameter!\n"}
}
#若只有一个参数，判断是否为“？”。若是则调用help函数，否则提示错误并退出

my @parameter_array = @ARGV;
die "Error:Missing \"-o\" parameter!\n" unless(grep(/^-o$/,@parameter_array));
#若参数中没有“-o”，提示错误并退出
my @words_array;
my @numbers_array;
my $filename;

while(@parameter_array){
#处理每一个参数
	my $parameter = shift @parameter_array;
	if($parameter eq '-w'){
		my $para = shift @parameter_array;
		&checkword($para);
		push @words_array,$para;
		#将“-w”后的字符串加入数组@words_array中
	}elsif($parameter eq '-d'){
		my $para = shift @parameter_array;
		&checknum($para);
		push @numbers_array,$para;
		#将“-d”后的数字加入到数组@numbers_array中
	}elsif($parameter eq '-o'){
		my $para = shift @parameter_array;
		$filename = $para;
		#将“-o”后的文件名赋值给$filename变量
	}else{
		die "Error:Parameter near \"$parameter\"\n";
		#若有其他类型的参数或者格式不正确则提示错误并退出
	}
}

my @everyword_listarray_ref_array;
foreach(@words_array){
#将每一个字符串参数生成全部大小写数组，并将该数组的引用存放在数组@everyword_listarray_ref_array中
	my $all_case_array_ref = &creat_all_case($_);
	push(@everyword_listarray_ref_array,$all_case_array_ref);
}
foreach(@numbers_array){
#将每一个数字参数放在单独的数组中，并将该数组的引用存放在数组@everyword_listarray_ref_array中
	my @number_array = ($_);
	my $number_array_ref = \@number_array;
	push(@everyword_listarray_ref_array,$number_array_ref);
}

open(PASSLIST,">$filename");
foreach(permute(@everyword_listarray_ref_array)){
#permute返回的是所有排列的数组的引用的数组
        array_permu(@$_);
	#对每一种数组排列调用函数
}
close PASSLIST;

sub array_permu{
#用来实现数组间的组合
        my $aa = shift @_;
        foreach(@$aa){
                push(@arr,$_);
                array_permu(@_) if @_;
		unless(@_){
                	print PASSLIST join('',@arr).$/;
		}
                pop @arr;
        }

}
sub creat_all_case{
#生成所有大小写的数组，返回该数组的引用
	my $word = shift @_;
	my @all_case_array;
	while($word){
		push(@all_case_array,$word);
		$word = &add1($word);
	}
	my $all_case_array_ref = \@all_case_array;
	return $all_case_array_ref;
}

sub add1{
#实现字母+1
        my ($words) = (@_);
        if($words =~ /^[A-Z]+$/){return 0}
        my @words_array = split('',$words);
        my $jinwei = 1;
        foreach (@words_array){
                if($jinwei == 1) {
                        if($_ =~ /^[a-z]$/){
                                $_ = uc $_;
                                $jinwei = 0;
                        }elsif($_ =~ /^[A-Z]$/){
                                $_ = lc $_;
                                $jinwei = 1;
                        }
                }
        }
        $words = join('',@words_array);
        return $words;
}

sub checkword{
#检查是否有其他字符
	my $word = shift @_;
	if ($word =~ /[^a-z]/){
		die "Error:Incorrect format near \"$word\"\n";
	}
}

sub checknum{
#检查是否有其他字符
	my $num = shift @_;
	if ($num =~ /[^0-9]/){
		die "Error:Incorrect format near \"$num\"\n";
	}
}
sub help{
	system "clear";
	print qq(Social Engineering Password Dictionary Builder(V1.0) - by adidala

	Usage:	./creatdict [? | -w xxx | -d xxx] -o xxx

	Parameter
	 ?:	To show this page
	-w:	A word,it will generate all combinations of case(Optional Repeatable)
	-d:	A number(Optional Repeatable)
	-o:	Output file of password dictionary(Essential)
		
	Example:
	./creatdict -w a -w b -d 1 -o pass1.txt
	then will generate a file named pass1.txt and contains:
	ab1
	aB1
	Ab1
	AB1
	a1b
	a1B
	A1b
	A1B
	ba1
	bA1
	Ba1
	BA1
	b1a
	b1A
	B1a
	B1A
	1ab
	1aB
	1Ab
	1AB
	1ba
	1bA
	1Ba
	1BA
	
);
	exit;
}

